package org.Script.ScriptTest;

import java.awt.*;
import java.beans.*;
import java.io.*;
import java.lang.reflect.*;
import java.util.*;
import java.util.List;
import javax.script.*;
import javax.swing.*;


/**
 * @author Cay Horstmann
 * @version 1.00 2007-10-28
 */
public class ScriptTest {
    public static void main(final String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                String language;
                if (args.length == 0) language = "js";
                else language = args[0];

                String jsLanguage = "Nashorn";

                ScriptEngineManager manager = new ScriptEngineManager();
                System.out.println("Available factories: ");
                ScriptEngine engine = null;
                for (ScriptEngineFactory factory : manager.getEngineFactories()) {
                    System.out.println(factory.getEngineName());
                    jsLanguage = factory.getEngineName();
                    engine = factory.getScriptEngine();
                    System.out.println("MimeTypes " + factory.getMimeTypes().toString());
                    System.out.println("Extensions " + factory.getExtensions().toString());
                    System.out.println("Names " + factory.getNames().toString());
                }

//                final ScriptEngine engine = manager.getEngineByName(jsLanguage);
//                final ScriptEngine engine = manager.getEngineFactories().get(0).getScriptEngine();
                if (engine == null) {
                    System.err.println("No engine for " + language);
                    System.exit(1);
                }

                ButtonFrame frame = new ButtonFrame();

                try {
                    String initPath = getResourcePath("init." + language);
                    if (initPath != null) {
                        File initFile = new File(initPath);
                        if (initFile.exists()) {
                            engine.eval(new FileReader(initFile));
                        }
                    }

                    getComponentBindings(frame, engine);

                    // =============================================
//                    Bindings scope = engine.createBindings();
//                    engine.put("b", new JButton());
//                    try {
//                        engine.eval("b.setText(\"hello world\")");
//                        engine.eval("panel.add(b)");
//                    } catch (ScriptException e) {
//                        throw new RuntimeException(e);
//                    }
                    // =============================================

                    final Properties events = new Properties();
                    events.load(getFileReaderFromResource(language + ".properties"));
                    for (final Object e : events.keySet()) {
                        String[] s = ((String) e).split("\\.");
                        addListener(s[0], s[1], (String) events.get(e), engine);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }

                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setTitle("ScriptTest");
                frame.setVisible(true);
            }
        });

    }

    private static String getResourcePath(String fileName) {
        String url = Objects.requireNonNull(ScriptTest.class.getClassLoader().getResource("")).getPath();
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append(url);
        stringBuilder.append(fileName);
        return stringBuilder.toString();
    }

    private static FileReader getFileReaderFromResource(String fileName) {
        //找到要读取目标文件的地址
        String path = getResourcePath(fileName);
        File file = new File(path);
        try {
            return new FileReader(file);
        } catch (FileNotFoundException exception) {
            System.out.printf("getFileReaderFromResource %s not exist. \n", path );
            return  null;
        }
    }

    /**
     * Gathers all named components in a container.
     *
     * @param c               the component
     * @param engine
     */
    private static void getComponentBindings(Component c, ScriptEngine engine) {
        String name = c.getName();
        if (name != null) engine.put(name, c);
        if (c instanceof Container) {
            for (Component child : ((Container) c).getComponents())
                getComponentBindings(child, engine);
        }
    }

    /**
     * Adds a listener to an object whose listener method executes a script.
     *
     * @param beanName   the name of the bean to which the listener should be added
     * @param eventName  the name of the listener type, such as "action" or "change"
     * @param scriptCode the script code to be executed
     * @param engine     the engine that executes the code
//     * @param bindings   the bindings for the execution
     */
    private static void addListener(String beanName, String eventName, final String scriptCode,
                                    final ScriptEngine engine) throws IllegalArgumentException, IntrospectionException,
            IllegalAccessException, InvocationTargetException {
        Object bean = engine.get(beanName);
        EventSetDescriptor descriptor = getEventSetDescriptor(bean, eventName);
        if (descriptor == null) return;
        descriptor.getAddListenerMethod().invoke(
                bean,
                Proxy.newProxyInstance(null, new Class[]{descriptor.getListenerType()},
                        new InvocationHandler() {
                            public Object invoke(Object proxy, Method method, Object[] args)
                                    throws Throwable {
                                engine.eval(scriptCode);
                                return null;
                            }
                        }));

    }

    private static EventSetDescriptor getEventSetDescriptor(Object bean, String eventName)
            throws IntrospectionException {
        for (EventSetDescriptor descriptor : Introspector.getBeanInfo(bean.getClass())
                .getEventSetDescriptors())
            if (descriptor.getName().equals(eventName)) return descriptor;
        return null;
    }
}

class ButtonFrame extends JFrame {
    public ButtonFrame() {
        setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);

        panel = new JPanel();
        panel.setName("panel");
        add(panel);

        yellowButton = new JButton("Yellow");
        yellowButton.setName("yellowButton");
        blueButton = new JButton("Blue");
        blueButton.setName("blueButton");
        redButton = new JButton("Red");
        redButton.setName("redButton");

        panel.add(yellowButton);
        panel.add(blueButton);
        panel.add(redButton);
    }

    public static final int DEFAULT_WIDTH = 300;
    public static final int DEFAULT_HEIGHT = 200;

    private JPanel panel;
    private JButton yellowButton;
    private JButton blueButton;
    private JButton redButton;
}
